#include "FontManager.hpp"

FontExporter::FontExporter(string filename)  : 
    texID_(0), listBase_(0),    // initalize GL variables to zero
    widths_(NUM_CHARS),         // make room for 96 widths
    height_(0), drawX_(0), drawY_(0)
{
	const size_t MARGIN = 3;
	cout << "FontExporter init ..." << endl;
	// library init
	if (FT_Init_FreeType(&m_library)) std::cout << "error during FreeType library init." << std::endl;
	// new face creation
	const char* filename_ = filename.c_str();
	if (FT_New_Face(m_library,filename_,0,&m_face) != 0) std::cout << "impossible to load font file." << std::endl;
	cout << "opening font file by FontExporter : Sucess." << endl;
	// testing if font is scalable
	if(!(m_face->face_flags & FT_FACE_FLAG_SCALABLE) || !(m_face->face_flags & FT_FACE_FLAG_HORIZONTAL))
		std::cout << "error : font size could not be set." << std::endl;
	m_size = 24;
	FT_Set_Pixel_Sizes(m_face, m_size,0);  

	// Step 2: Find maxAscent/Descent to calculate imageHeight //
	size_t imageHeight = 0;
	size_t imageWidth = 256;
	int maxDescent = 0;
	int maxAscent = 0;
	size_t lineSpace = imageWidth - MARGIN;
	size_t lines = 1;
	size_t charIndex;


	for(unsigned int ch = 0; ch < NUM_CHARS; ++ch) {
    	// Look up the character in the font file.
		charIndex = FT_Get_Char_Index(m_face, ch+SPACE);

	    // Render the current glyph.
	    FT_Load_Glyph(m_face, charIndex, FT_LOAD_RENDER);

	    widths_[ch] = (m_face->glyph->metrics.horiAdvance >> 6) + MARGIN;
        //cout << "width : " << widths_[ch] << endl;

	    // If the line is full go to the next line
	    if(widths_[ch] > lineSpace) {
	        lineSpace = imageWidth - MARGIN;
	        ++lines;
	    }
	    lineSpace -= widths_[ch];

	    maxAscent = std::max(m_face->glyph->bitmap_top, maxAscent);
	    maxDescent = std::max(m_face->glyph->bitmap.rows -
                    m_face->glyph->bitmap_top, maxDescent);
	}

	cout << "done..." << endl;
    height_ = maxAscent + maxDescent;   // calculate height_ for text

    // Compute how high the texture has to be.
    size_t neededHeight = (maxAscent + maxDescent + MARGIN) * lines + MARGIN;
    // Get the first power of two in which it will fit
	imageHeight = neededHeight;

    // Step 3: Generation of the actual texture //

    // create and zero the memory
    unsigned char* image = new unsigned char[imageHeight * imageWidth];

	for (unsigned int i = 0; i < imageHeight * imageWidth; i++) image[i] = 0;

    // These are the position at which to draw the next glyph
    size_t x = MARGIN;
    size_t y = MARGIN + maxAscent;
    float texX1, texX2, texY1, texY2;   // used for display list

    listBase_ = glGenLists(NUM_CHARS);  // generate the lists for filling

    // Drawing loop
    for(unsigned int ch = 0; ch < NUM_CHARS; ++ch)
    {
        size_t charIndex = FT_Get_Char_Index(m_face, ch+SPACE);

        // Render the glyph
        FT_Load_Glyph(m_face, charIndex, FT_LOAD_DEFAULT);
        FT_Render_Glyph(m_face->glyph, FT_RENDER_MODE_NORMAL);

        // See whether the character fits on the current line
        if(widths_[ch] > imageWidth - x)
        {
            x = MARGIN;
            y += (maxAscent + maxDescent + MARGIN);
        }

        // calculate texture coordinates of the character
        texX1 = static_cast<float>(x) / imageWidth;
        texX2 = static_cast<float>(x+widths_[ch]) / imageWidth;
        texY1 = static_cast<float>(y - maxAscent) / imageHeight;
        texY2 = texY1 + static_cast<float>(height_) / imageHeight;

        // generate the character's display list
        glNewList(listBase_ + ch, GL_COMPILE);
        glBegin(GL_QUADS);
        /*glTexCoord2f(texX1,texY1);  glVertex2i(0,0);
        glTexCoord2f(texX2,texY1);  glVertex2i(widths_[ch],0);
        glTexCoord2f(texX2,texY2);  glVertex2i(widths_[ch],height_);
        glTexCoord2f(texX1,texY2);  glVertex2i(0,height_);*/
        glTexCoord2f(texX1,texY2);  glVertex2i(0,0);
        glTexCoord2f(texX1,texY1);  glVertex2i(0,height_);
        glTexCoord2f(texX2,texY1);  glVertex2i(widths_[ch],height_);
        glTexCoord2f(texX2,texY2);  glVertex2i(widths_[ch],0);
        glEnd();
        glTranslatef(widths_[ch],0,0);  // translate forward
        glEndList();

        // copy image generated by FreeType to the texture
        for(int row = 0; row < m_face->glyph->bitmap.rows; ++row) {
            for(int pixel = 0; pixel < m_face->glyph->bitmap.width; ++pixel) {
                // set pixel at position to intensity (0-255) at the position
                image[(x + m_face->glyph->bitmap_left + pixel) +
                    (y - m_face->glyph->bitmap_top + row) * imageWidth] =
                        m_face->glyph->bitmap.buffer[pixel +
                            row * m_face->glyph->bitmap.pitch];
            }
        }

        x += widths_[ch];
    }

    // generate the OpenGL texture from the byte array
    glGenTextures(1, &texID_);
    glBindTexture(GL_TEXTURE_2D, texID_);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA8, imageWidth, imageHeight, 0,
                    GL_ALPHA, GL_UNSIGNED_BYTE, image);

    delete[] image;     // now done with the image memory
    FT_Done_Face(m_face); // free the face data

	cout << "Font export finished." << endl;

		};


float FontExporter::drawString(float x, float y, string str, float scale_)
{
    glBindTexture(GL_TEXTURE_2D, texID_);

    glPushMatrix();
    glTranslated(x,y,0);
	glScalef(scale_,scale_,scale_);

	float tmpWidth = 0.0;

    for(std::string::const_iterator i = str.begin(); i != str.end(); ++i)
    {
        unsigned char ch( *i - SPACE ); // ch-SPACE = DisplayList offset
        // replace characters outside the valid range with undrawable
        if(ch > NUM_CHARS)
        {
            ch = NUM_CHARS-1;   // last character is 'undrawable'
        }
		tmpWidth += widths_[ch];
 
        glCallList(listBase_+ch);    // calculate list to call
    }

    // Alternative, ignores undrawables (no noticable speed difference)
    //glListBase(listBase_-32);
    //glCallLists(static_cast<int>(std::strlen(buf)), GL_UNSIGNED_BYTE, buf);

    glPopMatrix();

	return tmpWidth;
}

float FontExporter::getStringLength(string str_) {
	float tmpWidth = 0.0;
    for(std::string::const_iterator i = str_.begin(); i != str_.end(); ++i)
    {
        unsigned char ch( *i - SPACE ); // ch-SPACE = DisplayList offset
        // replace characters outside the valid range with undrawable
        if(ch > NUM_CHARS) ch = NUM_CHARS-1;   // last character is 'undrawable'
		tmpWidth += widths_[ch];
    }
	return tmpWidth;
}

string FontExporter::getLimitedString(string str_, float length_) {
	float tmp = 0.0;
	int i;
	for (i = str_.size() - 1; i >= 0; i--) {
        unsigned char ch( str_[i] - SPACE ); // ch-SPACE = DisplayList offset
		if (tmp + widths_[ch] > length_) break;
		tmp += widths_[ch];
	}	
	return str_.substr(i+1);
}

int FontExporter::getLimitIndex(string str_, float length_) {
	float tmp = 0.0;
	int i;
	int lastSpace = -1;
	for (i = 0; i < str_.size(); i++) {
		if (str_[i] == ' ') lastSpace = i;
        unsigned char ch( str_[i] - SPACE ); // ch-SPACE = DisplayList offset
		if (tmp + widths_[ch] > length_) break;
		tmp += widths_[ch];
	}
	if (lastSpace > 0) return lastSpace;	
	else return i-1;
}


float FontExporter::drawBoundedString(float x, float y, string str_, float length_, float scale_){
	cout << "LME DEBUG : call of drawBoundedString for #" << str_ << "#" << endl;
	string tmp = str_;
	int end = 0; 
	float height = 0.0f;
	while (tmp != "") {
		int id = getLimitIndex(tmp, length_); 
		string display = tmp.substr(0,id+1);
		tmp = tmp.substr(id+1);
		cout << "LME DEBUG : substr display is #" << display << "#" << endl;
		drawString(x, y - height, display);
		height += height_;
	};
	return height + height_;
}


/********************************************************************************/
/*				FONT MANAGER CODE 												*/
/********************************************************************************/


map<string, FontExporter*> FontManager::m_fontMap;



FontExporter* FontManager::getFontExporter(string filename) {
	map<string, FontExporter*>::iterator it;
	it = m_fontMap.find(filename);
	if (it == m_fontMap.end()) {
		m_fontMap[filename] = new FontExporter(filename);
		return m_fontMap[filename];
	} else return m_fontMap[filename];
};
